﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Globalization;

namespace EditAnyString.Extensions
{
	public class GenericListTypeConverter<T> : TypeConverter
	{
		protected const char ElementSeparator = '|';

		protected readonly TypeConverter _typeConverter;

		public GenericListTypeConverter()
		{
			_typeConverter = TypeDescriptor.GetConverter( typeof( T ) );
			if( _typeConverter == null )
				throw new InvalidOperationException( "No type converter exists for type " + typeof( T ).FullName );
		}

		protected virtual string[] getStringArray( string input )
		{
			if( !String.IsNullOrEmpty( input ) )
			{
				string[] result = input.Split( ElementSeparator );
				Array.ForEach( result, s => s.Trim() );
				return result;
			}
			else
				return new string[ 0 ];
		}

		public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
		{

			if( sourceType == typeof( string ) )
			{
				string[] items = getStringArray( sourceType.ToString() );
				return ( items.Count() > 0 );
			}

			return base.CanConvertFrom( context, sourceType );
		}

		public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value )
		{
			if( value is string )
			{
				string[] items = getStringArray( (string)value );
				var result = new List<T>();
				Array.ForEach( items, s =>
				{
					object item = _typeConverter.ConvertFromInvariantString( s );
					if( item != null )
					{
						result.Add( (T)item );
					}
				} );

				return result;
			}
			return base.ConvertFrom( context, culture, value );
		}

		public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType )
		{
			if( destinationType == typeof( string ) )
			{
				string result = string.Empty;
				if( ( (IList<T>)value ) != null )
				{
					//we don't use string.Join() because it doesn't support invariant culture
					for( int i = 0; i < ( (IList<T>)value ).Count; i++ )
					{
						var str1 = Convert.ToString( ( (IList<T>)value )[ i ], CultureInfo.InvariantCulture );
						result += str1;
						//don't add comma after the last element
						if( i != ( (IList<T>)value ).Count - 1 )
							result += ElementSeparator;
					}
				}
				return result;
			}

			return base.ConvertTo( context, culture, value, destinationType );
		}
	}
}
